﻿using Engine;
using Engine.Graphics;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WorldRacing2.Terrain
{
    class CreateShadow
    {
        internal void CreateShadows()
        {
            terrain.StopUpdateThread();
            Vector3 viewDierection = -shaderManager.Lightdirection;// this.camera.CameraPosition - this.camera.CameraTartget;
            viewDierection.Normalize();

            for (int x = 0; x < this.terrain.nSectorsX * this.terrain.numberOfCellsPerSector; x++)
            {
                for (int y = 0; y < this.terrain.nSectorsY * this.terrain.numberOfCellsPerSector; y++)
                {
                    int vertexX = x;
                    int vertexY = y;

                    Engine.Graphics.VertexStructures.MyOwnVertex.PositionNormalTextured vertex = this.terrain.vertices[vertexX, vertexY];
                    Engine.Graphics.VertexStructures.MyOwnVertex.Wr2Data vertexWr2Data = (Engine.Graphics.VertexStructures.MyOwnVertex.Wr2Data)this.terrain.verticesWr2Data[vertexX, vertexY];
                    Color4 color = new Color4()
                    {
                        Alpha = 0.0f,
                        Red = vertexWr2Data.VertexColor.Red,
                        Green = vertexWr2Data.VertexColor.Green,
                        Blue = vertexWr2Data.VertexColor.Blue
                    };

                    vertexWr2Data.VertexColor = color;
                    this.terrain.vertices[vertexX, vertexY] = vertex;
                    Vector3 positionVertexOptimized = new Vector3()
                    {
                        X = this.terrain.vertices[vertexX, vertexY].Position.X,
                        Y = this.terrain.vertices[vertexX, vertexY].Position.Y + 200.0f,
                        Z = this.terrain.vertices[vertexX, vertexY].Position.Z
                    };

                    Vector3 vNear = positionVertexOptimized + (viewDierection * 15.0f);
                    Vector3 vFar = positionVertexOptimized + (viewDierection * 1000000.0f);

                    if (Vector3.Zero != terrain.GetPositionOnTerrain(vNear, vFar))
                    {
                        color.Alpha = 1.0f;
                        vertexWr2Data.VertexColor = color;
                        this.terrain.vertices[vertexX, vertexY] = vertex;
                    }

                    if (-1 != this.objectManager.IntersectObject(vNear, vFar))
                    {
                        color.Alpha = 1.0f;
                        vertexWr2Data.VertexColor = color;
                        this.terrain.vertices[vertexX, vertexY] = vertex;
                    }
                }
            }

            terrain.StartUpdateThread();
        }
        private Meshobject CreateShadowsKrom(int sectorX, int sectorY)
        {
            float viewDistance = 1090;
            Vector3 viewDierection = -shaderManager.Lightdirection;// this.camera.CameraPosition - this.camera.CameraTartget;
            viewDierection.Normalize();
            Meshobject terrainSectorMesh = this.sector[sectorX, sectorY].temporaryterrainMesh;
            Vector3 sectorPosition = this.sector[sectorX, sectorY].boundingBox.PositionCenter;

            int blocksOrCells = this.nSectorsX * this.nSectorsY;
            int numberOfBlocksX = this.nSectorsX;
            int numberOfBlocksY = this.nSectorsY;
            float[] BlockHi = new float[blocksOrCells];
            float[] BlockLo = new float[blocksOrCells];
            short[][] Trace2Sun = new short[blocksOrCells][];
            float SunX = -shaderManager.Lightdirection.X;
            float SunY = -shaderManager.Lightdirection.Y;
            float SunZ = -shaderManager.Lightdirection.Z;
            int cb = -1;

            for (int x = 0; x < numberOfBlocksX; x++)
            {
                for (int y = 0; y < numberOfBlocksY; y++)
                {
                    BlockHi[x + y * numberOfBlocksX] = this.sector[x, y].boundingBox.UpperLeft.Y;
                    BlockLo[x + y * numberOfBlocksX] = this.sector[x, y].boundingBox.LowerRight.Y;
                }
            }

            int ci = 0;
            for (int x = 0; x < numberOfBlocksX; x++)
            {
                for (int y = 0; y < numberOfBlocksY; y++)
                {
                    ci = 0;
                    Trace2Sun[x + y * numberOfBlocksX] = new short[50 + 1];
                    cb++;
                    int j = 0;
                    int e1, e2;
                    int[] Edge = new int[8];
                    int ID;

                    float BX = x;
                    float BZ = y;
                    int bx = 0, bz = 0;

                    float L1x = BX;
                    float L1y = BZ;
                    float L2x = BX;
                    float L2y = BZ + 1;       //23
                    float L3x = BX + 1;
                    float L3y = BZ + 1;
                    float L4x = BX + 1;
                    float L4y = BZ;   //14

                    float dx, dy;

                    float Sx = (float)Math.Round(BX + SunX * 1000000);
                    float Sy = (float)Math.Round(BX + SunZ * 1000000);

                    do
                    {
                        if (SunZ == 0)
                        {
                            if (SunX >= 0)
                                e1 = (int)GeoMath.EnsureRange(BX, 1, numberOfBlocksX);
                            else
                                e1 = 1;
                            if (SunX >= 0)
                                e2 = numberOfBlocksX;
                            else
                                e2 = (int)GeoMath.EnsureRange(BX, 1, numberOfBlocksX);
                        }
                        else
                        {
                            for (int i = 0; i < 1; i++)
                            {
                                //Lower-Higher border
                                Edge[1 + i * 4] = (int)Math.Truncate(L1x + ((BZ + j + i) - L1y) * (Sx - L1x) / (Sy - L1y)); //L1-S
                                Edge[2 + i * 4] = (int)Math.Truncate(L2x + ((BZ + j + i) - L2y) * (Sx - L2x) / (Sy - L2y)); //L2-S
                                Edge[3 + i * 4] = (int)Math.Truncate(L3x + ((BZ + j + i) - L3y) * (Sx - L3x) / (Sy - L3y)); //L3-S
                                Edge[4 + i * 4] = (int)Math.Truncate(L4x + ((BZ + j + i) - L4y) * (Sx - L4x) / (Sy - L4y)); //L4-S
                            }

                            if (SunX >= 0)
                            {
                                e1 = (int)GeoMath.EnsureRange(GeoMath.MinIntValue(Edge), (int)BX, numberOfBlocksX);
                                e2 = (int)GeoMath.EnsureRange(GeoMath.MaxIntValue(Edge), (int)BX, numberOfBlocksX);
                            }
                            else
                            {
                                e1 = (int)GeoMath.EnsureRange(GeoMath.MinIntValue(Edge), 1, (int)BX);
                                e2 = (int)GeoMath.EnsureRange(GeoMath.MaxIntValue(Edge), 1, (int)BX);
                            }
                        }

                        for (int m = e1; m < e2; m++)
                        {
                            ID = ((int)BZ + j) * numberOfBlocksX + m;
                            //Get distance between vertex and block
                            dx = GeoMath.GetLength(m - bx, j); //Check if block high enough and possibly can drop shadow on current block
                            dx = BlockLo[cb] + (256 * dx - 362) * (float)Math.Abs(Math.Tan(Math.Asin(SunY)));//store whole add-up in one value
                            //362^2=256^2+256^2
                            if (dx <= BlockHi[ID])
                            {
                                //allocate additional space
                                if (Trace2Sun[cb].Length < ci + 1)
                                    Array.Resize(ref Trace2Sun[cb], Trace2Sun[cb].Length + 50 + 1);

                                ci++;
                                Trace2Sun[cb][ci] = (short)((BZ + j - 1) * numberOfBlocksX + m);
                            }
                        }

                        if (SunZ >= 0)
                            j++;
                        else
                            j--;
                    } while (((SunZ > 0) && (BZ + j > numberOfBlocksY)) || ((SunZ < 0) && (BZ + j < 1)) || (SunZ == 0));

                    Array.Resize(ref Trace2Sun[cb], ci + 2);
                    Trace2Sun[cb][ci + 1] = 0;
                    Trace2Sun[cb][0] = (short)ci;
                }

                //Make a list of unique vertices.
                //Field contains either own vertice ID or reference to duplicate vertice.
                //List is reverse sorted, meaning all references are forward directed (to forward)
                //Vertice treated as unique only if it has unique XYZ, rest is not important
                //Form1.MemoLog.Lines.Add('Complete in '+ElapsedTime(@OldTime));

                for (int i = 0; i < terrainSectorMesh.Vertices.Length; i++)
                {
                    MyOwnVertex.Wr2Data vertex = (MyOwnVertex.Wr2Data)terrainSectorMesh.VerticesWr2Data[i];

                    vertex.VertexColor = new Color4()
                    {
                        Alpha = 112.0f / 255.0f,
                        Red = vertex.VertexColor.Red,
                        Green = 1.0f,// terrainSectorMesh.Vertices[i].VertexColor.Green,
                        Blue = vertex.VertexColor.Blue
                    };
                    terrainSectorMesh.VerticesWr2Data[i] = vertex;
                }

                for (int i = 0; i < terrainSectorMesh.Vertices.Length; i++)
                {
                    Vector3 vp = terrainSectorMesh.Vertices[i].Position;
                    Vector3 sp = vp + viewDierection * 10000000;
                    Vector3 vs = vp - sp; // Vector to sun. (x1-x2)

                    int bx = (int)GeoMath.EnsureRange((float)Math.Truncate(vp.X / Terrain.SECTORSIZE + this.nSectorsX / 2 + 1), 0, this.nSectorsX - 1);
                    int bz = (int)GeoMath.EnsureRange((float)Math.Truncate(vp.Z / Terrain.SECTORSIZE + this.nSectorsY / 2 + 1), 0, this.nSectorsY - 1);
                    int BlockID = (int)Math.Round((double)((bz) * this.nSectorsX + bx));

                    for (cb = 0; cb < Trace2Sun[BlockID].Length; cb++)
                    {
                        int tx = (Trace2Sun[BlockID][cb] - 1) % this.nSectorsX + 1;
                        int tz = (Trace2Sun[BlockID][cb] - 1) / this.nSectorsX + 1;

                        float dx = GeoMath.GetLength(tx - bx, tz - bz);//Check if block high enough and possibly can drop shadow on current block
                        float diagonalSizeOfSector = GeoMath.GetLength(1024, 1024);
                        dx = vp.Y + (dx - diagonalSizeOfSector) * (float)Math.Abs(Math.Tan(Math.Asin(viewDierection.Y)));//store whole add-up in one value

                        if (dx < BlockHi[Trace2Sun[BlockID][cb]])
                        {
                            do
                            {
                            }
                            while (false);
                        }
                    }
                }

                //
            }
            return terrainSectorMesh;
        }
    }
}